/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package top.infopub.security.filter;


import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import top.infopub.exception.ServiceException;


/**
 * Filter that allows access if the current user has the permissions specified by the mapped value, or denies access
 * if the user does not have all of the permissions specified.
 * </br>Authorization：授权器，或者访问控制器  授权，即权限验证，验证某个已认证的用户是否拥有某个权限；即判断用户是否能做事情，
 * 常见的如：验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限，如mgr对按钮权限的访问控制
 * @since 0.9
 */
public class PermissionsAuthorizationFilter extends AuthorizationFilter {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response,
                                   Object mappedValue)
        throws IOException {

        Subject subject = getSubject(request, response);
        // mappedValue 为权限码
        String[] perms = (String[])mappedValue;

        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {
            if (perms.length == 1) {
                if (!subject.isPermitted(perms[0])) {
                    isPermitted = false;
                }
            }
            else {
                for (String perm : perms) {
                    if (subject.isPermitted(perm)) {
                        isPermitted = true;
                        return isPermitted;
                    }
                }
            }
        }
        return isPermitted;
    }

    /** 当无操作权限时  **/
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
        throws IOException {

        Subject subject = getSubject(request, response);
        // If the subject isn't identified, redirect to login URL
        if (subject.getPrincipal() == null) {
            saveRequestAndRedirectToLogin(request, response);
        }
        else {
            // If subject is known but not authorized, redirect to the unauthorized URL if there is one
            // If no unauthorized URL is specified, just return an unauthorized HTTP status code
            String unauthorizedUrl = getUnauthorizedUrl();
            // SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit:
            if (StringUtils.hasText(unauthorizedUrl)) {
                // WebUtils.issueRedirect(request, response, unauthorizedUrl);
                // 这里不采用redirect，而使用forward，从而使unauthorizedUrl方法可以共享request对象  Awoke 2018-10-31
                RequestDispatcher requestDispatcher = request.getRequestDispatcher(unauthorizedUrl);
                try {
                    requestDispatcher.forward(request, response);
                }
                catch (ServletException e) {
                    log.error("", e);
                    throw new ServiceException("鉴权失败，跳转异常！");
                }
            }
            else {
                WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
            }
        }
        return false;
    }

}
